package ongd.servicios.persistencia;

import java.io.Serializable;
import javax.persistence.EntityManager;
import javax.persistence.EntityNotFoundException;
import ongd.negocio.gestion.Persona;
import ongd.negocio.gestion.Proyecto;
import java.util.ArrayList;
import java.util.List;
import ongd.negocio.gestion.Apadrinado;
import ongd.negocio.gestion.Cuota;
import ongd.negocio.gestion.Entidad;
import ongd.negocio.gestion.Fraccionado;
import ongd.servicios.persistencia.exceptions.IllegalOrphanException;
import ongd.servicios.persistencia.exceptions.NonexistentEntityException;
import ongd.servicios.persistencia.exceptions.PreexistingEntityException;

/**
 * Clase que contiene los método que para realizar operaciones
 * sobre la base de datos referente a la entidad persona.
 *
 * @author <a href="mailto:egi0002@alu.ubu.es">Elena García Iglesias</a>
 * @author <a href="mailto:cms0021@alu.ubu.es">Cristina Martínez Sanz</a>
 * @version 1.0 20120124
 *
 */
public class PersonaJpaController extends AJPAControlador implements Serializable {

    /**
     * Objeto de tipo EntityManager.
     */
    private EntityManager em = null;

    /**
     * Constructor de la clase.
     * @param em EntityManager
     */
    public PersonaJpaController(EntityManager em) {
        this.em = em;
    }

    /**
     * Método que crea un objeto de tipo persona en la base de datos.
     * @param entidad Objeto que crearemos 
     * @param et Booleano que indica si hay que iniciar la transacción
     * @throws PreexistingEntityException
     * @throws Exception 
     */
    @Override
    public void create(Entidad entidad, boolean et) throws PreexistingEntityException, Exception {
        Persona persona = (Persona) entidad;
        if (persona.getProyectoList() == null) {
            persona.setProyectoList(new ArrayList<Proyecto>());
        }
        if (persona.getApadrinadoList() == null) {
            persona.setApadrinadoList(new ArrayList<Apadrinado>());
        }
        if (persona.getCuotaList() == null) {
            persona.setCuotaList(new ArrayList<Cuota>());
        }
        if (persona.getFraccionadoList() == null) {
            persona.setFraccionadoList(new ArrayList<Fraccionado>());
        }
        try {
            //Si et es falso significa que tengo que crear la transacción
            if (!et) {
                em.getTransaction().begin();
            }
            //Solo recorremos la lista de proyectos si es padrino o socio-padrino
            if (persona.esPadrino()) {
                List<Proyecto> attachedProyectoList = new ArrayList<Proyecto>();
                for (Proyecto proyectoListProyectoToAttach : persona.getProyectoList()) {
                    proyectoListProyectoToAttach = em.getReference(proyectoListProyectoToAttach.getClass(), proyectoListProyectoToAttach.getIdProyecto());
                    attachedProyectoList.add(proyectoListProyectoToAttach);
                }
                persona.setProyectoList(attachedProyectoList);
                List<Apadrinado> attachedApadrinadoList = new ArrayList<Apadrinado>();
                for (Apadrinado apadrinadoListApadrinadoToAttach : persona.getApadrinadoList()) {
                    apadrinadoListApadrinadoToAttach = em.getReference(apadrinadoListApadrinadoToAttach.getClass(), apadrinadoListApadrinadoToAttach.getApadrinadoPK());
                    attachedApadrinadoList.add(apadrinadoListApadrinadoToAttach);
                }
                persona.setApadrinadoList(attachedApadrinadoList);
            }

            if (persona.esSocio()) {
                List<Cuota> attachedCuotaList = new ArrayList<Cuota>();
                for (Cuota cuotaListCuotaToAttach : persona.getCuotaList()) {
                    cuotaListCuotaToAttach = em.getReference(cuotaListCuotaToAttach.getClass(), cuotaListCuotaToAttach.getCuotaPK());
                    attachedCuotaList.add(cuotaListCuotaToAttach);
                }
                persona.setCuotaList(attachedCuotaList);
                List<Fraccionado> attachedFraccionadoList = new ArrayList<Fraccionado>();
                for (Fraccionado fraccionadoListFraccionadoToAttach : persona.getFraccionadoList()) {
                    fraccionadoListFraccionadoToAttach = em.getReference(fraccionadoListFraccionadoToAttach.getClass(), fraccionadoListFraccionadoToAttach.getFraccionadoPK());
                    attachedFraccionadoList.add(fraccionadoListFraccionadoToAttach);
                }
                persona.setFraccionadoList(attachedFraccionadoList);
            }
            em.persist(persona);
            if (persona.esPadrino()) {
                for (Proyecto proyectoListProyecto : persona.getProyectoList()) {
                    proyectoListProyecto.getPersonaList().add(persona);
                    proyectoListProyecto = em.merge(proyectoListProyecto);
                }
                for (Apadrinado apadrinadoListApadrinado : persona.getApadrinadoList()) {
                    Persona oldPersonaDNIOfApadrinadoListApadrinado = apadrinadoListApadrinado.getPersonaDNI();
                    apadrinadoListApadrinado.setPersonaDNI(persona);
                    apadrinadoListApadrinado = em.merge(apadrinadoListApadrinado);
                    if (oldPersonaDNIOfApadrinadoListApadrinado != null) {
                        oldPersonaDNIOfApadrinadoListApadrinado.getApadrinadoList().remove(apadrinadoListApadrinado);
                        oldPersonaDNIOfApadrinadoListApadrinado = em.merge(oldPersonaDNIOfApadrinadoListApadrinado);
                    }
                }
            }

            if (persona.esSocio()) {
                for (Cuota cuotaListCuota : persona.getCuotaList()) {
                    Persona oldPersonaOfCuotaListCuota = cuotaListCuota.getPersona();
                    cuotaListCuota.setPersona(persona);
                    cuotaListCuota = em.merge(cuotaListCuota);
                    if (oldPersonaOfCuotaListCuota != null) {
                        oldPersonaOfCuotaListCuota.getCuotaList().remove(cuotaListCuota);
                        oldPersonaOfCuotaListCuota = em.merge(oldPersonaOfCuotaListCuota);
                    }
                }
                for (Fraccionado fraccionadoListFraccionado : persona.getFraccionadoList()) {
                    Persona oldPersonaOfFraccionadoListFraccionado = fraccionadoListFraccionado.getPersona();
                    fraccionadoListFraccionado.setPersona(persona);
                    fraccionadoListFraccionado = em.merge(fraccionadoListFraccionado);
                    if (oldPersonaOfFraccionadoListFraccionado != null) {
                        oldPersonaOfFraccionadoListFraccionado.getFraccionadoList().remove(fraccionadoListFraccionado);
                        oldPersonaOfFraccionadoListFraccionado = em.merge(oldPersonaOfFraccionadoListFraccionado);
                    }
                }
            }
            //Si et es falso significa que tengo que terminar la transaccion
            if (!et) {
                em.getTransaction().commit();
            }
        } catch (Exception ex) {
            if (findEntidad(Persona.class, persona.getDni()) != null) {
                throw new PreexistingEntityException("Persona " + persona + " already exists.", ex);
            }
            throw ex;
        } finally {
            if (em != null) {
//                em.close();
            }
        }
    }

    /**
     * Método que edita un objeto de tipo persona en la base de datos.
     * @param entidad Entidad que editaremos
     * @param et Booleano que indica si hay que iniciar la transacción
     * @throws NonexistentEntityException
     * @throws IllegalOrphanException
     * @throws Exception 
     */
    @Override
    public void edit(Entidad entidad, boolean et) throws IllegalOrphanException, NonexistentEntityException, Exception {
        Persona persona = (Persona) entidad;
        try {
            //Si et es falso significa que tengo que crear la transacción
            if (!et) {
                em.getTransaction().begin();
            }
            Persona persistentPersona = em.find(Persona.class, persona.getDni());
            List<Proyecto> proyectoListOld = persistentPersona.getProyectoList();
            List<Proyecto> proyectoListNew = persona.getProyectoList();
            List<Apadrinado> apadrinadoListOld = persistentPersona.getApadrinadoList();
            List<Apadrinado> apadrinadoListNew = persona.getApadrinadoList();
            List<Cuota> cuotaListOld = persistentPersona.getCuotaList();
            List<Cuota> cuotaListNew = persona.getCuotaList();
            List<Fraccionado> fraccionadoListOld = persistentPersona.getFraccionadoList();
            List<Fraccionado> fraccionadoListNew = persona.getFraccionadoList();
            List<String> illegalOrphanMessages = null;
            for (Apadrinado apadrinadoListOldApadrinado : apadrinadoListOld) {
                if (!apadrinadoListNew.contains(apadrinadoListOldApadrinado)) {
                    if (illegalOrphanMessages == null) {
                        illegalOrphanMessages = new ArrayList<String>();
                    }
                    illegalOrphanMessages.add("You must retain Apadrinado " + apadrinadoListOldApadrinado + " since its personaDNI field is not nullable.");
                }
            }
            for (Cuota cuotaListOldCuota : cuotaListOld) {
                if (!cuotaListNew.contains(cuotaListOldCuota)) {
                    if (illegalOrphanMessages == null) {
                        illegalOrphanMessages = new ArrayList<String>();
                    }
                    illegalOrphanMessages.add("You must retain Cuota " + cuotaListOldCuota + " since its persona field is not nullable.");
                }
            }
            for (Fraccionado fraccionadoListOldFraccionado : fraccionadoListOld) {
                if (!fraccionadoListNew.contains(fraccionadoListOldFraccionado)) {
                    if (illegalOrphanMessages == null) {
                        illegalOrphanMessages = new ArrayList<String>();
                    }
                    illegalOrphanMessages.add("You must retain Fraccionado " + fraccionadoListOldFraccionado + " since its persona field is not nullable.");
                }
            }
            if (illegalOrphanMessages != null) {
                throw new IllegalOrphanException(illegalOrphanMessages);
            }
            List<Proyecto> attachedProyectoListNew = new ArrayList<Proyecto>();
            for (Proyecto proyectoListNewProyectoToAttach : proyectoListNew) {
                proyectoListNewProyectoToAttach = em.getReference(proyectoListNewProyectoToAttach.getClass(), proyectoListNewProyectoToAttach.getIdProyecto());
                attachedProyectoListNew.add(proyectoListNewProyectoToAttach);
            }
            proyectoListNew = attachedProyectoListNew;
            persona.setProyectoList(proyectoListNew);
            List<Apadrinado> attachedApadrinadoListNew = new ArrayList<Apadrinado>();
            for (Apadrinado apadrinadoListNewApadrinadoToAttach : apadrinadoListNew) {
                apadrinadoListNewApadrinadoToAttach = em.getReference(apadrinadoListNewApadrinadoToAttach.getClass(), apadrinadoListNewApadrinadoToAttach.getApadrinadoPK());
                attachedApadrinadoListNew.add(apadrinadoListNewApadrinadoToAttach);
            }
            apadrinadoListNew = attachedApadrinadoListNew;
            persona.setApadrinadoList(apadrinadoListNew);
            List<Cuota> attachedCuotaListNew = new ArrayList<Cuota>();
            for (Cuota cuotaListNewCuotaToAttach : cuotaListNew) {
                cuotaListNewCuotaToAttach = em.getReference(cuotaListNewCuotaToAttach.getClass(), cuotaListNewCuotaToAttach.getCuotaPK());
                attachedCuotaListNew.add(cuotaListNewCuotaToAttach);
            }
            cuotaListNew = attachedCuotaListNew;
            persona.setCuotaList(cuotaListNew);
            List<Fraccionado> attachedFraccionadoListNew = new ArrayList<Fraccionado>();
            for (Fraccionado fraccionadoListNewFraccionadoToAttach : fraccionadoListNew) {
                fraccionadoListNewFraccionadoToAttach = em.getReference(fraccionadoListNewFraccionadoToAttach.getClass(), fraccionadoListNewFraccionadoToAttach.getFraccionadoPK());
                attachedFraccionadoListNew.add(fraccionadoListNewFraccionadoToAttach);
            }
            fraccionadoListNew = attachedFraccionadoListNew;
            persona.setFraccionadoList(fraccionadoListNew);
            persona = em.merge(persona);
            for (Proyecto proyectoListOldProyecto : proyectoListOld) {
                if (!proyectoListNew.contains(proyectoListOldProyecto)) {
                    proyectoListOldProyecto.getPersonaList().remove(persona);
                    proyectoListOldProyecto = em.merge(proyectoListOldProyecto);
                }
            }
            for (Proyecto proyectoListNewProyecto : proyectoListNew) {
                if (!proyectoListOld.contains(proyectoListNewProyecto)) {
                    proyectoListNewProyecto.getPersonaList().add(persona);
                    proyectoListNewProyecto = em.merge(proyectoListNewProyecto);
                }
            }
            for (Apadrinado apadrinadoListNewApadrinado : apadrinadoListNew) {
                if (!apadrinadoListOld.contains(apadrinadoListNewApadrinado)) {
                    Persona oldPersonaDNIOfApadrinadoListNewApadrinado = apadrinadoListNewApadrinado.getPersonaDNI();
                    apadrinadoListNewApadrinado.setPersonaDNI(persona);
                    apadrinadoListNewApadrinado = em.merge(apadrinadoListNewApadrinado);
                    if (oldPersonaDNIOfApadrinadoListNewApadrinado != null && !oldPersonaDNIOfApadrinadoListNewApadrinado.equals(persona)) {
                        oldPersonaDNIOfApadrinadoListNewApadrinado.getApadrinadoList().remove(apadrinadoListNewApadrinado);
                        oldPersonaDNIOfApadrinadoListNewApadrinado = em.merge(oldPersonaDNIOfApadrinadoListNewApadrinado);
                    }
                }
            }
            for (Cuota cuotaListNewCuota : cuotaListNew) {
                if (!cuotaListOld.contains(cuotaListNewCuota)) {
                    Persona oldPersonaOfCuotaListNewCuota = cuotaListNewCuota.getPersona();
                    cuotaListNewCuota.setPersona(persona);
                    cuotaListNewCuota = em.merge(cuotaListNewCuota);
                    if (oldPersonaOfCuotaListNewCuota != null && !oldPersonaOfCuotaListNewCuota.equals(persona)) {
                        oldPersonaOfCuotaListNewCuota.getCuotaList().remove(cuotaListNewCuota);
                        oldPersonaOfCuotaListNewCuota = em.merge(oldPersonaOfCuotaListNewCuota);
                    }
                }
            }
            for (Fraccionado fraccionadoListNewFraccionado : fraccionadoListNew) {
                if (!fraccionadoListOld.contains(fraccionadoListNewFraccionado)) {
                    Persona oldPersonaOfFraccionadoListNewFraccionado = fraccionadoListNewFraccionado.getPersona();
                    fraccionadoListNewFraccionado.setPersona(persona);
                    fraccionadoListNewFraccionado = em.merge(fraccionadoListNewFraccionado);
                    if (oldPersonaOfFraccionadoListNewFraccionado != null && !oldPersonaOfFraccionadoListNewFraccionado.equals(persona)) {
                        oldPersonaOfFraccionadoListNewFraccionado.getFraccionadoList().remove(fraccionadoListNewFraccionado);
                        oldPersonaOfFraccionadoListNewFraccionado = em.merge(oldPersonaOfFraccionadoListNewFraccionado);
                    }
                }
            }
            //Si et es falso significa que tengo que terminar la transaccion
            if (!et) {
                em.getTransaction().commit();
            }
        } catch (Exception ex) {
            String msg = ex.getLocalizedMessage();
            if (msg == null || msg.length() == 0) {
                String id = persona.getDni();
                if (findEntidad(Persona.class, id) == null) {
                    throw new NonexistentEntityException("The persona with id " + id + " no longer exists.");
                }
            }
            throw ex;
        } finally {
            if (em != null) {
//                em.close();
            }
        }
    }

    /**
     * Método que borra un objeto de tipo persona de la base de datos.
     * @param clase Clase correpondiente al objeto a borrar
     * @param id Clave primaria del objeto a borrar
     * @param et Booleano que indica si hay que iniciar la transaccion
     * @throws NonexistentEntityException 
     * @throws IllegalOrphanException
     */
    @Override
    public void destroy(Class clase, Object id, boolean et) throws IllegalOrphanException, NonexistentEntityException {
        try {
            //Si et es falso significa que tengo que crear la transacción
            if (!et) {
                em.getTransaction().begin();
            }
            Persona persona;
            try {
                persona = (Persona) em.getReference(clase, id);
                persona.getDni();
            } catch (EntityNotFoundException enfe) {
                throw new NonexistentEntityException("The persona with id " + id + " no longer exists.", enfe);
            }
            List<String> illegalOrphanMessages = null;
            List<Apadrinado> apadrinadoListOrphanCheck = persona.getApadrinadoList();
            for (Apadrinado apadrinadoListOrphanCheckApadrinado : apadrinadoListOrphanCheck) {
                if (illegalOrphanMessages == null) {
                    illegalOrphanMessages = new ArrayList<String>();
                }
                illegalOrphanMessages.add("This Persona (" + persona + ") cannot be destroyed since the Apadrinado " + apadrinadoListOrphanCheckApadrinado + " in its apadrinadoList field has a non-nullable personaDNI field.");
            }
            List<Cuota> cuotaListOrphanCheck = persona.getCuotaList();
            for (Cuota cuotaListOrphanCheckCuota : cuotaListOrphanCheck) {
                if (illegalOrphanMessages == null) {
                    illegalOrphanMessages = new ArrayList<String>();
                }
                illegalOrphanMessages.add("This Persona (" + persona + ") cannot be destroyed since the Cuota " + cuotaListOrphanCheckCuota + " in its cuotaList field has a non-nullable persona field.");
            }
            List<Fraccionado> fraccionadoListOrphanCheck = persona.getFraccionadoList();
            for (Fraccionado fraccionadoListOrphanCheckFraccionado : fraccionadoListOrphanCheck) {
                if (illegalOrphanMessages == null) {
                    illegalOrphanMessages = new ArrayList<String>();
                }
                illegalOrphanMessages.add("This Persona (" + persona + ") cannot be destroyed since the Fraccionado " + fraccionadoListOrphanCheckFraccionado + " in its fraccionadoList field has a non-nullable persona field.");
            }
            if (illegalOrphanMessages != null) {
                throw new IllegalOrphanException(illegalOrphanMessages);
            }
            List<Proyecto> proyectoList = persona.getProyectoList();
            for (Proyecto proyectoListProyecto : proyectoList) {
                proyectoListProyecto.getPersonaList().remove(persona);
                proyectoListProyecto = em.merge(proyectoListProyecto);
            }
            em.remove(persona);
            //Si et es falso significa que tengo que terminar la transaccion
            if (!et) {
                em.getTransaction().commit();
            }
        } finally {
            if (em != null) {
//                em.close();
            }
        }
    }

    /**
     * Método que busca una persona en la base de datos.
     * @param clase Class correspondiente al objeto a buscar
     * @param id Clave primaria del objeto a buscar
     * @return persona
     */
    @Override
    public Persona findEntidad(Class clase, Object id) {
        try {
            return (Persona) em.find(clase, id);
        } finally {
//            em.close();
        }
    }
}
